Skip to content

[PWCI] "vhost: VDUSE-related fixes"#607

Open
ovsrobot wants to merge 3 commits intomainfrom
series_36991
Open

[PWCI] "vhost: VDUSE-related fixes"#607
ovsrobot wants to merge 3 commits intomainfrom
series_36991

Conversation

@ovsrobot
Copy link
Owner

@ovsrobot ovsrobot commented Jan 8, 2026

Auto-submission for "http://patchwork.dpdk.org/project/dpdk/list/?series=36991"

Summary by Sourcery

Harden vhost VDUSE and control virtqueue handling and align vring limits with the maximum queue pair configuration.

Bug Fixes:

  • Guard virtio-net control virtqueue descriptor traversal with bounds checks to avoid invalid or looping descriptor chains.
  • Fix VDUSE IOTLB mmap error handling by correctly checking for MAP_FAILED and reporting the underlying errno.

Enhancements:

  • Derive the maximum vring count from the configured maximum queue pairs (2 data queues per pair plus one control queue) and use this constant consistently for virtqueue arrays.

Summary by CodeRabbit

  • Bug Fixes

    • Improved error handling for memory mapping operations with more detailed error reporting.
    • Added stricter validation and bounds checking for descriptor chain processing to prevent invalid operations.
  • Refactor

    • Adjusted internal queue configuration calculations for better alignment with system limits.

✏️ Tip: You can customize this high-level summary in your review settings.

When max_queue_pairs is set to VHOST_MAX_QUEUE_PAIRS (128), VDUSE
calculates total_queues as max_queue_pairs * 2 + 1 = 257 to account
for the control queue. However, the virtqueue array was sized as
VHOST_MAX_QUEUE_PAIRS * 2, causing an out-of-bounds array access.

Fix by defining VHOST_MAX_VRING to explicitly account for the control
queue (VHOST_MAX_QUEUE_PAIRS * 2 + 1) and using it for the virtqueue
array size.

Fixes: f0a37cc6a1e2 ("vhost: add multiqueue support to VDUSE")
Cc: stable@dpdk.org

Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>
Signed-off-by: 0-day Robot <robot@bytheb.org>
The virtio_net_ctrl_pop() function traverses descriptor chains from
guest-controlled memory without validating that the descriptor index
stays within bounds and without a counter to prevent infinite loops
from circular chains.

A malicious guest could craft descriptors with a next field pointing
out of bounds causing memory corruption, or create circular descriptor
chains causing an infinite loop and denial of service.

Add bounds checking and a loop counter to both descriptor chain
traversal loops, similar to the existing protection in virtio_net.c
fill_vec_buf_split().

Fixes: 474f4d7 ("vhost: add control virtqueue")
Cc: stable@dpdk.org

Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>
Signed-off-by: 0-day Robot <robot@bytheb.org>
The mmap() function returns MAP_FAILED on failure, not NULL.
The current check for !mmap_addr will never detect mmap failures.

When mmap fails but the error is not detected, an invalid address (-1)
is inserted into the IOTLB cache via vhost_user_iotlb_cache_insert().
Subsequent attempts to access this address will cause memory
corruption or crash.

Fix by checking for MAP_FAILED instead of NULL. Also add strerror to
the error message for easier debugging.

Fixes: f27d520 ("vhost: add VDUSE callback for IOTLB miss")
Cc: stable@dpdk.org

Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>
Signed-off-by: 0-day Robot <robot@bytheb.org>
@sourcery-ai
Copy link

sourcery-ai bot commented Jan 8, 2026

Reviewer's Guide

This PR fixes VDUSE-related issues in the vhost library by hardening virtio control queue descriptor walking, correcting error handling for IOTLB mmaps, and aligning maximum vring counts and virtqueue array sizing with the supported number of queue pairs plus the control queue.

Updated class diagram for vhost vring and virtqueue sizing

classDiagram
    class VHOST_MAX_QUEUE_PAIRS {
        <<macro>>
        +0x80
    }

    class VHOST_MAX_VRING {
        <<macro>>
        +VHOST_MAX_QUEUE_PAIRS * 2 + 1
        +Comment: 2 vrings per queue pair plus 1 control queue
    }

    class vhost_virtqueue {
        <<struct>>
        +... // fields not changed in this PR
    }

    class virtio_net {
        <<struct>>
        +int extbuf
        +int linearbuf
        +vhost_virtqueue* virtqueue[VHOST_MAX_VRING]
        +rte_rwlock_t iotlb_pending_lock
        +vhost_iotlb_entry* iotlb_pool
    }

    VHOST_MAX_QUEUE_PAIRS <|-- VHOST_MAX_VRING : defines
    VHOST_MAX_VRING <.. virtio_net : array_size
    vhost_virtqueue <.. virtio_net : contains_array_of
Loading

File-Level Changes

Change Details Files
Harden virtio control virtqueue descriptor chain traversal to avoid overruns and invalid chains.
  • Introduce nr_descs and cnt counters to track the number of descriptors in the control vring, both for split and packed virtqueues.
  • Set nr_descs from the descriptor buffer length for shadow descs and from cvq->size when using the live vring descriptors.
  • Add bounds and loop-iteration checks (desc_idx >= nr_descs or cnt > nr_descs) in descriptor-walking loops, logging an error and bailing out on invalid chains instead of potentially overrunning.
  • Refactor the loop that searches for the writable descriptor to use a guarded while(1) with the same safety checks.
lib/vhost/virtio_net_ctrl.c
Fix VDUSE IOTLB mmap error detection and improve logging diagnostics.
  • Change mmap error check from a null-pointer test to checking against MAP_FAILED, which is the correct failure indicator.
  • Extend the error log message to include strerror(errno) for more useful diagnostics when IOTLB mmap fails.
lib/vhost/vduse.c
Align maximum vring constants and virtqueue array sizing with queue pair configuration and control queue.
  • Redefine VHOST_MAX_VRING from a fixed 0x100 to an expression based on VHOST_MAX_QUEUE_PAIRS * 2 + 1 to account for RX/TX per queue pair plus one control queue.
  • Update the virtio_net::virtqueue array size to use VHOST_MAX_VRING instead of VHOST_MAX_QUEUE_PAIRS * 2, ensuring space for the control queue vring as well.
lib/vhost/vhost.h

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link

coderabbitai bot commented Jan 8, 2026

📝 Walkthrough

Walkthrough

This PR improves VHOST backend robustness by refining IOTLB mmap error detection, redefining the maximum vring count macro based on queue pair configuration, and adding descriptor chain validation with bounds checking and safety counters to prevent invalid descriptor traversal.

Changes

Cohort / File(s) Summary
VHOST Configuration Updates
lib/vhost/vhost.h
Redefines VHOST_MAX_VRING macro from fixed literal (0x100) to computed value (VHOST_MAX_QUEUE_PAIRS * 2 + 1). Updates virtio_net structure's virtqueue array to use new VHOST_MAX_VRING dimension instead of VHOST_MAX_QUEUE_PAIRS * 2. Adds explanatory comment for calculation intent.
Error Handling Improvements
lib/vhost/vduse.c
Refines IOTLB mmap failure detection by checking for MAP_FAILED instead of null pointer. Enhances error logging to include IOVA value and strerror(errno) details while maintaining existing error recovery path.
Descriptor Chain Validation
lib/vhost/virtio_net_ctrl.c
Introduces descriptor chain bounds validation with nr_descs tracking across both indirect and direct descriptor paths. Computes nr_descs from indirect descriptor length (desc_len / sizeof(struct vring_desc)) or uses cvq->size for direct path. Adds safety counter and generalized loop structure (while (1)) with explicit break conditions to prevent excessive iterations. Strengthens descriptor index bounds checking with error logging before processing.

Poem

🐰 A rabbit hops through vring arrays,
Bounds-checking in all the right ways,
No more endless chains to roam,
Safe descriptors lead us home! 🎯

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'vhost: VDUSE-related fixes' accurately summarizes the main changes: three distinct fixes to VDUSE vhost support covering virtqueue array sizing, descriptor chain validation, and mmap error handling.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cd60dcd and 5acd8a1.

📒 Files selected for processing (3)
  • lib/vhost/vduse.c
  • lib/vhost/vhost.h
  • lib/vhost/virtio_net_ctrl.c
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Sourcery review
🔇 Additional comments (9)
lib/vhost/vhost.h (2)

265-266: LGTM! Correct fix for control queue array sizing.

The redefinition of VHOST_MAX_VRING from the fixed value 0x100 (256) to (VHOST_MAX_QUEUE_PAIRS * 2 + 1) (257) correctly accounts for the control queue. With VHOST_MAX_QUEUE_PAIRS = 0x80, the previous value of 256 would cause out-of-bounds access when accessing the control queue at index max_queue_pairs * 2 when max_queue_pairs equals VHOST_MAX_QUEUE_PAIRS.


505-505: Correctly updated array dimension to match new macro.

The virtqueue array now properly uses VHOST_MAX_VRING which includes space for the control queue, preventing out-of-bounds access when dev->cvq = dev->virtqueue[max_queue_pairs * 2] is assigned in vduse_device_create().

lib/vhost/vduse.c (1)

89-92: Critical fix: Correct mmap failure detection.

The previous check against NULL was incorrect since mmap() returns MAP_FAILED (defined as (void *)-1) on failure, not NULL. The old code would have missed actual mmap failures and proceeded to insert an invalid address ((void *)-1) into the IOTLB cache via vhost_user_iotlb_cache_insert(), potentially causing memory corruption or crashes when the address is later dereferenced.

Adding strerror(errno) to the error message is a good improvement for diagnostics.

lib/vhost/virtio_net_ctrl.c (6)

31-31: Good additions for descriptor chain validation.

The new nr_descs and cnt variables properly support bounds checking and loop detection for descriptor chain traversal.


44-47: Proper bounds validation on initial descriptor index.

This check prevents out-of-bounds access to cvq->desc[] when a malicious or corrupted guest provides an invalid descriptor index. The error path correctly advances last_avail_idx to avoid re-processing the same invalid entry.


62-67: Correct bounds computation for both descriptor paths.

For indirect descriptors, nr_descs is correctly derived from the descriptor table length. For direct descriptors, using cvq->size as the bound is appropriate since descriptor indices must be within the ring size.


69-73: Effective protection against malicious descriptor chains.

The dual check guards against:

  1. Out-of-bounds access: desc_idx >= nr_descs prevents reading beyond the descriptor array
  2. Infinite loops: ++cnt > nr_descs detects circular descriptor chains that would otherwise loop forever

This mirrors similar protections in virtio_net.c as noted in the PR objectives.


159-167: Consistent bounds checking in second traversal pass.

The cnt counter is properly reset before the second loop, and the same validation logic is applied. The loop structure change to while(1) with explicit breaks for VRING_DESC_F_WRITE is cleaner and maintains the safety guarantees established in the first pass.


152-156: Consistent nr_descs recomputation in second pass.

The bounds are correctly recalculated for the second traversal pass, ensuring both passes use identical validation logic.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • The new descriptor-chain guard logic in virtio_net_ctrl_pop() uses uint16_t for nr_descs and cnt while nr_descs is derived from a 64-bit desc_len; consider using a wider type (e.g., uint32_t/size_t) to avoid truncation and ensure the bounds checks are effective for larger descriptor tables.
  • The descriptor-chain validation pattern (nr_descs/cnt checks and error logging) is now duplicated in two places in virtio_net_ctrl.c; consider factoring this into a small helper to keep the logic consistent and easier to maintain.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new descriptor-chain guard logic in virtio_net_ctrl_pop() uses uint16_t for nr_descs and cnt while nr_descs is derived from a 64-bit desc_len; consider using a wider type (e.g., uint32_t/size_t) to avoid truncation and ensure the bounds checks are effective for larger descriptor tables.
- The descriptor-chain validation pattern (nr_descs/cnt checks and error logging) is now duplicated in two places in virtio_net_ctrl.c; consider factoring this into a small helper to keep the logic consistent and easier to maintain.

## Individual Comments

### Comment 1
<location> `lib/vhost/virtio_net_ctrl.c:60-63` </location>
<code_context>
 			goto err;
 		}

+		nr_descs = desc_len / sizeof(struct vring_desc);
 		desc_idx = 0;
 	} else {
</code_context>

<issue_to_address>
**suggestion:** Validate that the indirect descriptor length is a non-zero multiple of sizeof(struct vring_desc) before computing nr_descs.

Per the virtio spec, indirect descriptor tables must be non-zero and aligned to `sizeof(struct vring_desc)`. Relying on integer truncation means malformed tables (zero length or not a multiple) collapse into `nr_descs == 0` and are only caught later as a generic invalid chain. Instead, consider explicitly checking `desc_len == 0 || desc_len % sizeof(struct vring_desc) != 0` and failing early with a specific error log for malformed indirect tables.

```suggestion
		}

		/* Validate indirect descriptor table length before using it */
		if (unlikely(desc_len == 0 ||
			     desc_len % sizeof(struct vring_desc) != 0)) {
			VHOST_CONFIG_LOG(dev->ifname, ERR,
				"Malformed indirect descriptor table");
			goto err;
		}

		nr_descs = desc_len / sizeof(struct vring_desc);
		desc_idx = 0;
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants